/***********************
 * ParticleSystem class
 ***********************/

/**
 * The particle system class simply "manages" a collection of particles.
 * Its primary responsibility is to run the simulation, evolving particles
 * over time according to the applied forces using Euler's method.
 * This header file contains the functions that you are required to implement.
 * (i.e. the rest of the code relies on this interface)
 * In addition, there are a few suggested state variables included.
 * You should add to this class (and probably create new classes to model
 * particles and forces) to build your system.
 */

#ifndef __PARTICLE_SYSTEM_H__
#define __PARTICLE_SYSTEM_H__

#include <vector>
#include "vec.h"
#include "particle.h"
#include "fixedparticle.h"
#include "force.h"
#include "particleObject.h"
#include "pointParticleObject.h"
#include "sparkleParticleObject.h"
#include "earParticleObject.h"
#include <map>

const Vec3d GRAVITY_ACCELERATION(0, -0.8, 0);
const double DRAG_COEFF_AIR = 0.2;
const double DRAG_COEFF_EAR = 0.7;

const double SPARKLE_SPRING_DAMPING = 2.0;
const double SPARKLE_SPRING_CONSTANT = 2.0;
const int SPARKLE_NUM_POINTS = 4;
const double DISTANCE_BETWEEN_SPARKLES[SPARKLE_NUM_POINTS-1] = {0.05, 0.250, 0.375};
const double SPARKLES_MASS[SPARKLE_NUM_POINTS] = {5.0, 0.10, 0.05, 0.05};

const double EAR_SPRING_DAMPING = 1.0;
const double EAR_SPRING_CONSTANT = 3.0;
const int EAR_NUM_POINTS = 3;
const double DISTANCE_BETWEEN_EAR[EAR_NUM_POINTS-1] = {0.05, 0.075};
const double EAR_MASS[EAR_NUM_POINTS] = {0.8, 0.4, 0.2};
const Vec3d EAR_STABILIZER_GRAVITY(0, 2.5, 0);

class ParticleObject;

class ParticleSystem {

public:

	/** Constructor **/
	ParticleSystem();

	/** Destructor **/
	virtual ~ParticleSystem();

	/** Simulation fxns **/
	// This fxn should render all particles in the system,
	// at current time t.
	virtual void drawParticles(float t);

	// This fxn should save the configuration of all particles
	// at current time t.
	virtual void bakeParticles(float t);

	// This function should compute forces acting on all particles
	// and update their state (pos and vel) appropriately.
	virtual void computeForcesAndUpdateParticles(float t);

	// This function should reset the system to its initial state.
	// When you need to reset your simulation, PLEASE USE THIS FXN.
	// It sets some state variables that the UI requires to properly
	// update the display.  Ditto for the following two functions.
	virtual void resetSimulation(float t);

	// This function should start the simulation
	virtual void startSimulation(float t);

	// This function should stop the simulation
	virtual void stopSimulation(float t);

	// This function should clear out your data structure
	// of baked particles (without leaking memory).
	virtual void clearBaked();	

	// These accessor fxns are implemented for you
	float getBakeStartTime() { return bake_start_time; }
	float getBakeEndTime() { return bake_end_time; }
	float getBakeFps() { return bake_fps; }
	bool isSimulate() { return simulate; }
	bool isDirty() { return dirty; }
	void setDirty(bool d) { dirty = d; }

	// fxns to create and manipulate the system from outside
	void SpawnBall(Vec3d initialPosition, Vec3d initialVelocity, double mass = 1.0);
	void SpawnSparkle(Vec3d initialPosition, Vec3d initialVelocity);
	FixedParticle* SpawnEar(Vec3d initialPosition, Vec3d initialVelocity);

protected:
	
	/** Some baking-related state **/
	float bake_fps;						// frame rate at which simulation was baked
	float bake_start_time;				// time at which baking started 
										// These 2 variables are used by the UI for
										// updating the grey indicator 
	float bake_end_time;				// time at which baking ended
	bool baked;							// flag for baked particles

	/** General state variables **/
	bool simulate;						// flag for simulation mode
	bool dirty;							// flag for updating ui (don't worry about this)
	map<float, vector<Vec3d> > bakedParticles;


private:

	void updateParticle(Particle& p, double timeStep);

	// Data structures to store elements in our particle system
	vector<Particle*> particleList; // stores all particles (EXCLUDING) fixed ones
	vector<FixedParticle*> fixedParticleList; // stores only fixed particles
	vector<ParticleObject*> objectList;
	vector<Force*> forceList;

	ConstantGravitationalField* gravity;
	DragForce* viscousDrag;
	SpringForce* sparkleSpring; // spring effect for the sparklers
	SpringForce* earSpring;
	ConstantGravitationalField* earStabilizer; // to stabilize the position of the ear
	DragForce* earDrag;
	
};

#endif	// __PARTICLE_SYSTEM_H__
